/*********************************************************************************************
	Fichier: Terrain.c
	Ensemble des fonctions permettant d'initialiser, d'afficher et de supprimer le terrain
	et des fonctions permettant de supprimer des lignes, de déplacer et ajouter des pièces.
**********************************************************************************************/

#include <assert.h>
#include "Terrain.h"

Terrain* TerrainCreer(void)
{
	Terrain* sortie = (Terrain*)malloc(sizeof(Terrain));
	return sortie;
}


void TerrainDetruire(Terrain * t)
{
	int i;
	for ( i = TAILLE_Y - 1 ; i >=0 ; i-- )
	{
		TerrainSupprimerLigne(t, i); /*! on supprime d'abord ligne par ligne */
	}
	free(t);/* et ensuite on libère le terrain */
}

void TerrainInitialiser(Terrain* t)
{
	t->dimX = TAILLE_X;  /* on initialise la largeur du terrain */
	t->dimY = TAILLE_Y;  /* on initialise la hauteur du terrain */

	int i;
	int j;
	for ( i = 0 ; i < TAILLE_X; i++ ) /*! on parcours le terrain et on l'initialise à NULL */
	{
		for ( j = 0 ; j < TAILLE_Y; j++ )
		{
			t->ter[i][j] = NULL;
		}
	}
}

int TerrainPositionEstCorrecte(int x, int y)
{
	if ( x < 0)
	{
	    return 0; /* on ne peut pas être à gauche du terrain */
	}
	if ( x > TAILLE_X - 1 )
	{
	    return 0; /* on ne peut pas être à droite du terrain */
	}
 	if ( y > TAILLE_Y - 1 )
 	{
 	    return 0; /* on ne peut pas être en bas du terrain */
 	}
 	if ( y < -4 )
 	{
 	    return 0;/* la piece peut se trouver en partie en haut du terrain, au minimum pour y=-4, c'est le cas quand une nouvelle pièce arrive pendant le jeu */
 	}
	return 1;
}

Piece * TerrainGetPieceXY(Terrain* t, int x, int y)
{
	assert(x>=0); /* on vérifie que x est bien compris dans le terrain, pas de débordements sur la gauche */
	assert(x<t->dimX);/* on vérifie que x est bien compris dans le terrain, pas de débordements sur la droite */
	assert(y>=0); /* on vérifie que y est bien compris dans le terrain, pas de débordements sur le haut */
	assert(y<t->dimY);/* on vérifie que y est bien compris dans le terrain, pas de débordements sur le bas */
	return t->ter[x][y]; /* si x et y sont bien définis dans le terrain alors on renvoie l'adresse de la pièce dans le terrain*/
}

int TerrainGetDimX(Terrain* t)
{
	return t->dimX;
}

int TerrainGetDimY(Terrain* t)
{
	return t->dimY;
}

/* pour TerrainSupprimerLigne on fait un pointeur sur les pièces qui se trouve dans la ligne grâce à une boucle, on supprime la ligne dans la pièce,
et si le pointeur est vide alors on le libère et on recommence la boucle,
et si le pointeur n'est pas vide on creer la nouvelle pièce déformée afin d'enlever la ligne supprimée */
void TerrainSupprimerLigne (Terrain* t, int l)
{
	int x; /* x variable utilisée pour parcourir la ligne à supprimer*/
	for ( x = 0 ; x < TAILLE_X ; x++ )
	{
		Piece * rencontree = t->ter[x][l]; /* pointeur sur la piece occupant la position (x,l)*/
		if ( rencontree != NULL ) /* si la case est effectivement occupée, logique car on la ligne est pleine */
		{
			
		
			/* la position de la piece*/
			int p_x = rencontree->posXpiece;
			int p_y = rencontree->posYpiece;
			

			int position_ligne_dans_piece = l - p_y; /* pour connaitre la position de la ligne pleine  */
			
			int i; /* variable utilisée pour parcourir la ligne à supprimer */

			for ( i = 0 ; i < MAX_TAILLE_PIECE_Y ; i++ )
			{
				int position_colonne_dans_terrain = p_x + i;
				if ( rencontree->forme[i][position_ligne_dans_piece] == 1 )
				{
					/* élement de la piece est sur la ligne, il faut le supprimer */
					t->ter[position_colonne_dans_terrain][l] = NULL;
					rencontree->forme[i][position_ligne_dans_piece] = 0;
				}
			}
			/* on a fini de traiter la piece */
			if ( FormeEstVide(rencontree) == 1 ) /* la piece est vide, on la libere de la mémoire */
			{
				
				FormeDetruirePiece(rencontree);
			}
			else /* la piece n'est pas vide, il reste des 1 en haut et/ou en bas de la ligne supprimée, la pièce est donc surement coupée en 2 */
			{
				
				Piece * nouvelle = FormeCreerPiece();
				
				int partie_basse[MAX_TAILLE_PIECE_X][MAX_TAILLE_PIECE_Y] = {{0}};
				int X, Y;
				for ( X = 0 ; X < MAX_TAILLE_PIECE_X ; X++ )
					for ( Y = position_ligne_dans_piece ; Y < MAX_TAILLE_PIECE_Y ; Y++ )
					{
						
						if ( rencontree->forme[X][Y] == 1 )
						{
							rencontree->forme[X][Y] = 0;
							partie_basse[Y][X] = 1;
							t->ter[X + p_x][Y + p_y] = nouvelle;
						}
					}
				FormeInitialiserPiece(nouvelle, rencontree->couleur, partie_basse);
				nouvelle->posXpiece = p_x;
				nouvelle->posYpiece = p_y;
			
				if ( FormeEstVide(rencontree) == 1 ) /*la piece est vide, on la libere de la mémoire*/
				{
					
					FormeDetruirePiece(rencontree);
				}
				if ( FormeEstVide(nouvelle) == 1 ) /*la piece est vide, on la libere de la mémoire*/
				{
					
					FormeDetruirePiece(nouvelle);
				}
			}
		}
	}

}

int TerrainChercherLignePleine(Terrain* t, int * tab)/* ajoute deux arguments */
{
	int i;
	int j;
	int compteur;
	int nblignepleine;
	nblignepleine = 0;
	for ( j = 0 ; j < TAILLE_Y ; j++) /* parcours terrain et compte le nombre de case non vide */
	{
		compteur = 0;
		for ( i = 0 ; i < TAILLE_X ; i++ )
		{
			if ( t->ter[i][j] != 0 )
			{
			    compteur++;
			}
		}
		if (compteur == TAILLE_X ) /* si le nombre de non vide égale à la dimension de la largeur du tableau, la ligne est pleine */
		{
		    tab[nblignepleine]=j; /* stocke dans un tableau le numero de la ligne pleine */
		    nblignepleine++; /* compte le nombre de ligne pleine et le retourne */
		}
	}
	return nblignepleine;
}

int TerrainAjouterPiece(Terrain* t, Piece* p)
{
	p->posXpiece = (TAILLE_X-MAX_TAILLE_PIECE_X)/2; /* place la pièce au milieu des abscisses*/
	return TerrainDeplacerPiece(p, t, BAS);
}

int TerrainDeplacerPiece(Piece* p, Terrain* t, Direction d)
{
	int dirx=0;
	int diry=0;
	if (TerrainTestCollision(p,t,d) == 1)/* on fait quelque chose si la collision est possible */
	{
		switch(d)
		{
			case BAS: diry=1;
			break;
			case DROITE: dirx=1;
			break;
			case GAUCHE: dirx=-1;
			break;
			default: break;
		};

		/* on indique dans la structure de la piece que sa position a changée */
		p->posXpiece += dirx;
		p->posYpiece += diry;

		/* mais il faut aussi l'indiquer sur le terrain */
		int i,j;
		for ( i = 0 ; i < MAX_TAILLE_PIECE_X; i++ )
			for ( j = 0 ; j < MAX_TAILLE_PIECE_Y; j++ )
			{
				if ( p->forme[i][j] == 1 && (i + p->posXpiece - dirx >= 0) && (j + p->posYpiece - diry >= 0))/*! on teste si la case dans la pièce n'est pas vide et qu'elle la position actuelle de la case est supérieure ou égale à 0 */
				{
					t->ter[i + p->posXpiece - dirx][j + p->posYpiece - diry] = NULL; /* dans ce cas à cet endroit on met le terrain à null */
				}
			}
		for ( i = 0 ; i < MAX_TAILLE_PIECE_X; i++ )
			for ( j = 0 ; j < MAX_TAILLE_PIECE_Y; j++ )
			{
				if ( p->forme[i][j] == 1 && (i + p->posXpiece >= 0) && (j + p->posYpiece >= 0))/* on teste si la case dans la pièce n'est pas vide et que sa nouvelle position de la case est supérieure ou égale à 0 */
				{
					t->ter[i + p->posXpiece][j + p->posYpiece] = p; /* on met la pièce à cet endroit */
				}
			}
		return 1;
	}
	else
	{
		return 0;
	}
}

int TerrainTestCollision(Piece* p, Terrain* t, Direction d)
{
	int x;
	x = p->posXpiece;
	int y;
	y = p->posYpiece;
	int i;
	int j;


	switch(d)
	{
		case BAS: y+=1;
		break;
		case DROITE: x+=1;
		break;
		case GAUCHE: x-=1;
		break;
		default: break;
	};

	for ( i = 0 ; i < MAX_TAILLE_PIECE_X; i++ )
		for ( j = 0 ; j < MAX_TAILLE_PIECE_Y; j++ )
		{
			if ( p->forme[i][j] == 1 ) /* si la pièce est non vide en (i,j) */
			{
				int positionreelleX;
				positionreelleX = x + i;
				int positionreelleY;
				positionreelleY = y + j;
				if (TerrainPositionEstCorrecte(positionreelleX,positionreelleY))/* on vérifie que la pièce reste dans le terrain une fois avec le déplacement */
				{
					if (positionreelleY >= 0 && ( t->ter[positionreelleX][positionreelleY] != NULL ) && ( t->ter[positionreelleX][positionreelleY] != p )) /* on vérifie si le terrain n'est pas vide et différent de la pièce on renvoit que c'est impossible */
					{
						return 0;
					}
					else
					{

					}
				}
				else
				{
					return 0;
				}
			}
			else /* si la pièce est vide en (i,j) il n'y a rien à vérifier */
			{
				
			}
 		}
	return 1;
}

void TerrainEffacer(Terrain* t)
{
	int i;
	int j;
	for ( i = 0 ; i < TAILLE_X ; i++)   /* on parcours le terrain */
		for ( j = 0 ; j < TAILLE_Y ; j++)
		{
            		t->ter[i][j]=0; /* Ainsi on rempli le tableau de cases vides, il est donc effacé */
		}
}

void TerrainAfficher(Terrain* t)
{
	int i;
	int j;
    printf("\n\t|");
    for ( i = 0 ; i < TAILLE_X ; i++)
    {
        printf("%d|",i);/* on affiche le numéro de la colonne */
    }
    printf("\n");

	for ( j = 0 ; j < TAILLE_Y ; j++)
	{
		printf("%d\t|",j);/* on affiche le numéro de la ligne */
		for ( i = 0 ; i < TAILLE_X ; i++)
		{
			if ( t->ter[i][j] == NULL ) printf(" |");
			else printf("%d|",t->ter[i][j]->couleur);/*! lorsque la case du tableau est vide on affiche une |, si elle est pleine on affiche le numéro de la couleur */
		}
	printf("\n");
	}
}

int TerrainRotation(Piece * p, Terrain * t)
{
	if ( TerrainTestRotation(p, t) )/* si la rotation est possible */
	{
        int i,j;
        for ( i = 0 ; i < MAX_TAILLE_PIECE_X; i++ )
        {
            for ( j = 0 ; j < MAX_TAILLE_PIECE_Y; j++ )
            {
                if ( p->forme[i][j] == 1 && (i + p->posXpiece >= 0) && (j + p->posYpiece >= 0))/* si la case de la pièce n'est pas vide et que la position actuelle de la case est supérieure ou égale à 0 */
                {
                    t->ter[i + p->posXpiece][j + p->posYpiece] = NULL;/* on met le terrain à null à cet endroit */
                }
            }
        }
        FormeRotationPiece(p);/*! on fait la rotation */
        for ( i = 0 ; i < MAX_TAILLE_PIECE_X; i++ )
        {
            for ( j = 0 ; j < MAX_TAILLE_PIECE_Y; j++ )
            {
                if ( p->forme[i][j] == 1 && (i + p->posXpiece >= 0) && (j + p->posYpiece >= 0) )/* si la case de la pièce n'est pas vide et que la position actuelle de la case est supérieure ou égale à 0 */
                {
                    t->ter[i + p->posXpiece][j + p->posYpiece] = p;/* on met à cet endroit dans le terrain la pièce */
                }
            }
        }
        return 1;
	}
	else return 0;
}

int TerrainTestRotation(Piece * p, Terrain * t)
{
	FormeRotationPiece(p);

	int x = p->posXpiece;
	int y = p->posYpiece;
	int i, j;
	for ( i = 0 ; i < MAX_TAILLE_PIECE_X; i++ )
	{
        for ( j = 0 ; j < MAX_TAILLE_PIECE_Y; j++ )
        {
            if ( p->forme[i][j] == 1 ) /* si la pièce est non vide en (i,j) */
			{
				int positionreelleX;
				positionreelleX = x + i;
				int positionreelleY;
				positionreelleY = y + j;
				if (TerrainPositionEstCorrecte(positionreelleX,positionreelleY))/*! on vérifie que la pièce reste dans le terrain */
				{
					if (positionreelleY >= 0 && ( t->ter[positionreelleX][positionreelleY] != NULL ) && ( t->ter[positionreelleX][positionreelleY] != p )) /* on vérifie si le terrain n'est pas vide on renvoit que c'est impossible */
					{
						FormeRotationPiece(p);FormeRotationPiece(p);FormeRotationPiece(p);
						return 0;
					}
					else
					{
					
					}
				}
				else
				{
					FormeRotationPiece(p);FormeRotationPiece(p);FormeRotationPiece(p);
					return 0;
				}
			}
			else /* si la pièce est vide en (i,j) il n'y a rien à vérifier */
			{
				
			}
        }
	}
	FormeRotationPiece(p);FormeRotationPiece(p);FormeRotationPiece(p);
	return 1;
}

int TerrainAppliquerPesanteur(Terrain * t)
{
	int i, j;
	int deplacement_fait = 0;
	for ( j = TAILLE_Y - 1 ; j >= 0 ; j--)
	{
		for ( i = TAILLE_X - 1 ; i >= 0 ; i--)
		{
			if ( (t->ter[i][j] != NULL) ) /* on vérifie si la case visitée n'est pas vide */
				if ( TerrainDeplacerPiece(t->ter[i][j],t,BAS) )/* on parcours le terrain et si c'est possible pour chaque parcours on fait déplacer la pièce */
				{
					deplacement_fait = 1;
				}
		}
	}
	return deplacement_fait;
}

